package chapter9;

import redis.clients.jedis.Jedis;

public class LimitRequestDemo {
    /**
     * 主函数，用于演示如何使用Jedis操作Redis进行限流控制
     * 该函数首先连接到本地的Redis服务器，然后删除已有的请求类型键，
     * 接着循环5次，每次调用canVisit方法来模拟一次访问请求，并进行限流判断
     *
     * @param args 命令行参数，本例中未使用
     */
    public static void main(String[] args) {
        // 创建Jedis实例，连接本地Redis服务器
        Jedis jedis = new Jedis("localhost", 6379);
        // 定义请求类型为支付请求
        String requestType = "PayRequest";
        // 删除现有的请求类型键，以确保开始时环境干净
        jedis.del(requestType);
        // 循环5次，模拟5次访问请求
        for (int i = 0; i < 5; i++) {
            // 调用canVisit方法，传入Jedis实例，请求类型，资源限制（100），以及时间窗口（3秒）
            canVisit(jedis, requestType, 100, 3);
        }
    }


    /**
     * 判断是否可以访问
     * 使用Redis的有序集合zset来记录请求的时间戳，通过清除超过时间窗口的请求记录来限制请求频率。
     * 如果在指定时间窗口内的请求数量超过限制，则提示请求过于频繁。
     *
     * @param jedis       Jedis实例，用于操作Redis数据库
     * @param requestType 请求类型，用于区分不同类型的请求并分别限流
     * @param limitTime   单位为秒的时间窗口长度，用于定义多久算作一次请求
     * @param limitCount  在指定时间窗口内允许的最大请求数量
     */
    public static void canVisit(Jedis jedis, String requestType, int limitTime, int limitCount) {
        // 获取当前时间戳
        long currentTimeMillis = System.currentTimeMillis();
        // 将当前时间戳添加到指定请求类型的有序集合中
        jedis.zadd(requestType, currentTimeMillis, Long.toString(currentTimeMillis));
        // 移除超过时间窗口的请求时间戳
        jedis.zremrangeByScore(requestType, 0, currentTimeMillis - limitTime * 1000);
        // 获取当前时间窗口内的请求数量
        Long totalCount = jedis.zcard(requestType);
        // 为请求类型的集合设置过期时间，防止数据溢出
        jedis.expire(requestType, limitTime + 1);
        // 判断请求数是否超过限制
        if (totalCount > limitCount) {
            System.out.println("请求过于频繁，请稍后再试");
        } else {
            System.out.println("请求成功");
        }
    }

}
